# coding=utf-8
# 时间：2023/6/5 14:27

import cv2
import os
from matplotlib import pyplot as plt
import numpy as np
from PIL import Image, ImageDraw, ImageFont

#模板
template = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
            'X', 'Y', 'Z',
            '藏', '川', '鄂', '甘', '赣', '贵', '桂', '黑', '沪', '吉', '冀', '津', '晋', '京', '辽', '鲁', '蒙', '闽', '宁',
            '青', '琼', '陕', '苏', '皖', '湘', '新', '渝', '豫', '粤', '云', '浙']


num1 = 0
def img_show(image):
    global num1
    num1 += 1
    plt.figure(num1)
    plt.imshow(image, 'gray')
    plt.show()

def img_guss(image):
    image = cv2.GaussianBlur(image, (3, 3), 0)
    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    return gray_image

def get_BlueImg_bin(img):
    # 掩膜：BGR通道，若像素B分量在 100~255 且 G分量在 0~190 且 R分量在 0~140 置255（白色） ，否则置0（黑色）
    mask_gbr = cv2.inRange(img, (100, 0, 0), (255, 190, 140))
    img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)  # 转换成 HSV 颜色空间
    h, s, v = cv2.split(img_hsv)  # 分离通道  色调(H)，饱和度(S)，明度(V)
    mask_s = cv2.inRange(s, 80, 255)  # 取饱和度通道进行掩膜得到二值图像
    rgbs = cv2.bitwise_and(mask_gbr, mask_s)  # 与操作，两个二值图像都为白色才保留，否则置黑

    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (17, 7))
    kernelx = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
    rgbs = cv2.erode(rgbs, kernelx, 3) #腐蚀细小条纹
    img_rgbs_dilate = cv2.dilate(rgbs, kernel, 3)  # 膨胀 ，减小车牌空洞
    return img_rgbs_dilate

                #------------车牌定位------------#
img = cv2.imread("C:/Users/vv/Pictures/chepai5.jpg")
#高斯平滑
img_gaus = cv2.GaussianBlur(img, (5, 5), 0)
new = get_BlueImg_bin(img_gaus)
img_show(new)
#查找轮廓
a, b, c = cv2.findContours(new, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
new_img = []
det_x_max = 0
det_y_max = 0
num = 0
#获得最大的外接矩形
for i in range(len(b)):
    x_min = np.min(b[i][:, :, 0])
    x_max = np.max(b[i][:, :, 0])
    y_min = np.min(b[i][:, :, 1])
    y_max = np.max(b[i][:, :, 1])
    det_x = x_max - x_min
    det_y = y_max - y_min
    if (det_x / det_y > 2.0) and (det_x > det_x_max) and (det_y > det_y_max):
        det_y_max = det_y
        det_x_max = det_x
        num = i
# 获取最可疑区域轮廓点集
points = np.array(b[num][:, 0])
# 获取最小外接矩阵，中心点坐标，宽高，旋转角度
rect = cv2.minAreaRect(points)
# 获取矩形四个顶点，浮点型
box = cv2.boxPoints(rect)
# 取整
box = np.int0(box)
# 获取四个顶点坐标
left_point_x = np.min(box[:, 0])
right_point_x = np.max(box[:, 0])
top_point_y = np.min(box[:, 1])
bottom_point_y = np.max(box[:, 1])
min1 = []
#对x坐标排序
min1 = np.sort(box[:, 0], axis=-1)
vertices = []
#获得排序后x各自对应的点
for i in range(4):
    vertices.append([min1[i],box[:, 1][np.where(box[:, 0] == min1[i])][0]])

#确定上下左右四个点
if vertices[0][1] < vertices[1][1]:
    zuoshang = [min1[0],vertices[0][1]]
    zuoxia = [min1[0],vertices[1][1]]
else:
    zuoshang = [min1[1],vertices[1][1]]
    zuoxia = [min1[0],vertices[0][1]]
if vertices[2][1] < vertices[3][1]:
    youshang = [min1[2],vertices[2][1]]
    youxiaxia = [min1[3],vertices[3][1]]
else:
    youshang = [min1[3],vertices[3][1]]
    youxiaxia = [min1[2],vertices[2][1]]

# print(zuoshang,zuoxia,youshang,youxiaxia)
src = np.float32([zuoshang,zuoxia,youshang,youxiaxia]) #变换前的点
#变换后的点
dst = np.float32([[0, 0],[0,zuoxia[1]-zuoshang[1]],[youshang[0]-zuoshang[0],0],[youshang[0]-zuoshang[0],zuoxia[1]-zuoshang[1]]])
M = cv2.getPerspectiveTransform(src, dst) # 获得变换矩阵
#仿射变换
new_img = cv2.warpPerspective(img,M,(youshang[0]-zuoshang[0], zuoxia[1]-zuoshang[1]))
plt.imshow(new_img)
plt.show()
                #------------确定边界和消除锚点------------#
gray_image = img_guss(new_img)
img_show(gray_image)
ret, image = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 3))
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 1))
#膨胀
image = cv2.dilate(image, kernel1)
#腐蚀
image = cv2.erode(image, kernel2)

#上边界
top = 0 #上边界坐标
top_border = [0]*image.shape[0]
#记录每一行灰度值的变换
for row in range(image.shape[0]):
    for colum in range(image.shape[1]-1):
        if image[row, colum] != image[row, colum+1]:
            top_border[row] += 1
#根据记录的数据确定上边界
for row in range(image.shape[0]-2):
    if top_border[row] < 12:
        continue
    elif top_border[row+1] < 12:
        continue
    elif top_border[row + 2] < 12:
        continue
    else:
        top = row
        break

#下边界
bottom = 0 #下边界坐标
bottom_border = [0]*image.shape[0]
#记录每一行灰度值的变换
for row in range(image.shape[0]-1, -1, -1):
    for colum in range(image.shape[1]-1):
        if image[row, colum] != image[row, colum+1]:
            bottom_border[row] += 1
#根据记录的数据确定下边界
for row in range(image.shape[0]-1, 1, -1):
    if bottom_border[row] < 14:
        continue
    elif bottom_border[row - 1] < 14:
        continue
    elif bottom_border[row - 2] < 14:
        continue
    else:
        bottom = row
        break

#左边界
left = 0 #左边界坐标
left_border = [0]*image.shape[1]
#记录每一列灰度值的变换
for colum in range(image.shape[1]):
    for row in range(image.shape[0]-1):
        if image[row, colum] != image[row + 1, colum]:
            left_border[colum] += 1
#根据记录的数据确定左边界
for colum in range(image.shape[1]):
    if left_border[colum] < 3:
        continue
    elif left_border[colum+1] < 3:
        continue
    elif left_border[colum+2] < 3:
        continue
    else:
        left = colum
        break

#右边界
right = 0 #右边界坐标
right_border = [0]*image.shape[1]
#记录每一列灰度值的变换
for colum in range(image.shape[1]-1, -1, -1):
    for row in range(image.shape[0]-1):
        if image[row, colum] != image[row + 1, colum]:
            right_border[colum] += 1
#根据记录的数据确定右边界
for colum in range(image.shape[1]-1, 1, -1):
    if right_border[colum] < 2:
        continue
    elif right_border[colum-1] < 2:
        continue
    elif right_border[colum-2] < 2:
        continue
    else:
        right = colum
        break
#新的车牌图
border_image = image[top:bottom+1, left+1:right+4]
img_show(border_image)


                # ------------字符分割------------#
#创建一个纯黑背景图片，为投影做准备
black = np.zeros([border_image.shape[0], border_image.shape[1], 3],np.uint8)
black = cv2.cvtColor(black, cv2.COLOR_RGB2GRAY)

colum_count = [0]*border_image.shape[1] #存储每一列灰度值为255的点数
#记录每一列灰度值为255的点数
for colum in range(border_image.shape[1]):
    for row in range(border_image.shape[0]):
        if border_image[row, colum] == 255:
            colum_count[colum] += 1

#根据每一列灰度值为255的点数画投影
for colum in range(border_image.shape[1]):
    for row in range(colum_count[colum]):
        black[border_image.shape[0]-row-1, colum] = 255

start = [] #存储每一个字符的开始坐标
end = [] #存储每一个字符的结束坐标
in_block = True #是否进入边界标志
for colum in range(border_image.shape[1]):
    #进入边界
    if black[border_image.shape[0]-1, colum] == 255 and in_block:
        in_block = False
        if colum != 0:
            start.append(colum-1)
        else:
            start.append(colum)
    #退出边界
    elif black[border_image.shape[0]-1, colum] == 0 and (not in_block):
        in_block = True
        end.append(colum)


index = []
#寻找并消除锚点的坐标
print(start, end)
for i in range(len(end)):
    if end[i] - start[i] < 12:
        index.append(i)

j = 0
#去除不符合长度的边界点
if len(index) == 1:
    del start[index[0]], end[index[0]]
else:
    for i in range(len(index)):
        del start[index[i]-j], end[index[i]-j]
        j += 1

#以防汉字偏旁不连接
if end[0] - start[0] < 15:
    del end[0], start[1]
print("变换后",start,end)


#划分出各个字符
car_no_list = []
for i in range(len(end)):
    plt.subplot(1, len(end), i+1)
    roiNo = border_image[ : , start[i]:end[i]]
    car_no_list.append(roiNo)
    plt.imshow(border_image[ : , start[i]:end[i]], cmap='gray')
plt.show()



                    # ------------字符识别------------#
# 读取模板文件，读取template文件下的模板
def read_template_file(directory_name):
    template_list = []
    for fileName in os.listdir(directory_name):
        template_list.append(directory_name + "/" + fileName)
    return template_list

# 读取template文件下的模板，开始到结束
def get_template(start, end):
    template_words = []
    for i in range(start, end):
        word = read_template_file("D:/资料/Opencv/template/" + template[i])
        template_words.append(word)
    return template_words

def get_car_No(image, start, end):
    # 模板匹配
    best_scores = []
    template_lists = get_template(start, end)

    for template_list in template_lists:  # 每个文件夹
        scores = []
        for word in template_list:  # 一个文件夹下的多个模板
            template_file = cv2.imdecode(np.fromfile(word, dtype=np.uint8), 1)
            template_img = cv2.cvtColor(template_file, cv2.COLOR_RGB2GRAY)
            ret, template_img = cv2.threshold(template_img, 0, 255, cv2.THRESH_OTSU)
            height, width = template_img.shape[:2]

            carNo = cv2.resize(image, (width, height)) #将图片设置成统一大小
            # macthTemplate中图片要与模板尺寸一样大小
            result = cv2.matchTemplate(carNo, template_img, cv2.TM_CCOEFF)
            (_, score, _, _) = cv2.minMaxLoc(result)  # 获得分值
            scores.append(score)
        best_scores.append(max(scores))

    index = best_scores.index(max(best_scores))  # 分值最大的索引
    return template[start + index]  # 起始序号  索引

car_all_no = []
# 第一个汉字
first_chinese = get_car_No(car_no_list[0], 34, 64)
car_all_no.append(first_chinese)
# 第二个英文字母
second_english = get_car_No(car_no_list[1], 10, 33)
car_all_no.append(second_english)
# 数字及英文字母
for car_no in car_no_list[2:]:
    number_english = get_car_No(car_no, 0, 33)
    car_all_no.append(number_english)
print("车牌号为：%s" % "".join(car_all_no))

# 显示中文
def cv2ImgAddText(img, text, left, top, textColor=(255, 255, 0), textSize=50):
    if (isinstance(img, np.ndarray)):
        img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img)
    fontText = ImageFont.truetype("font/STSONG.TTF", textSize, encoding="utf-8")  # 字体
    draw.text((left, top), text, textColor, font=fontText)
    return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)

result_img = cv2ImgAddText(img, "".join(car_all_no), 10, 20)
result_img = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)
img_show(result_img)
plt.show()
# cv_show('result_img', result_img)


